import { useState, useRef, useReducer, useMemo, useCallback, useEffect, memo } from "react"
import { Tabs, Button, Input, Table } from "antd"
function Demo8() {
    // 1. useState  用于声明数据，数据改变会触发组件重新渲染。useState的参数是初始值，返回一个数组，第一个元素是当前值，第二个元素是更新函数
    const [num, setData] = useState(0) // 声明state数据
    const updateData = () => setData(num + 1) // 更新state数据
    const [num2, setNum2] = useState(0) // 声明state数据
    const updateNum2 = () => setNum2(num2 + 1) // 更新state数据

    // 2. useRef  用于声明一个可变的对象，数据改变不会触发组件重新渲染。常用于获取DOM元素或者保存一个数据，这个数据在组件重新渲染时不会丢失。
    const inputRef = useRef(null)

    // 3. useReducer  // 用于处理复杂的state逻辑，通过dispatch函数触发state更新，类似于redux。
    const [state, dispatch] = useReducer(reducerFn, { count: 0 }) // reducerFn不要用函数表达式写在下面，函数表达式不会提升
    function reducerFn(state, action) {
        switch (action.type) {
            case "increment":
                return { count: state.count + 1 }
            case "decrement":
                return { count: state.count - 1 }
            default:
                return state
        }
    }

    // 4. useMemo  useMemo用于缓存计算结果，只有当依赖项改变时才会重新计算(其他依赖改变导致的组件重新渲染，不会触发计算)。可以避免不必要的计算，提高性能。
    const [arr, setArr] = useState([1, 2])
    const memoizedValue = useMemo(() => arr.reduce((a, b) => a + b, 0), [arr])

    // 5. useCallback  useCallback用于缓存函数，只有当依赖项改变时才会重新生成函数。可以避免不必要的函数重新生成，提高性能。
    const memoizedCallback = useCallback(() => {
        console.log("callback执行", num)
        updateData()
    }, [num])
    // 无依赖,每次都重新创建函数，等于未使用useCallback;  每次打印的num都是最新值，useCallback失去作用，子组件重新渲染
    // 非空数组,仅依赖变化时才会重新创建函数，            每次打印都是最新值，useCallback失去作用，子组件重新渲染
    // 空数组,仅第一次创建函数                          每次打印都是初始值，子组件不会重新渲染

    // 6. useEffect  useEffect用于处理副作用，如数据请求、订阅、手动修改DOM等。useEffect的第二个参数是依赖项，当依赖项改变时才会执行副作用函数。useEffect的返回值是一个函数，用于清除副作用。
    // 用法1： 无依赖
    useEffect(() => {
        // console.log("无依赖，组件初始化/更新时执行1次，相当于vue的created updated")
        return () => {
            // console.log("无依赖，组件卸载/更新时执行1次，相当于vue的destroyed updated")
        }
    })

    // 用法2： 依赖项改变时执行
    useEffect(() => {
        // console.log("依赖项改变/依赖更新时执行")
    }, [num])

    // 用法3： 仅在初始化时执行
    useEffect(() => {
        console.log("仅在初始化时执行1次")
    }, [])

    const TableColumns = [
        {
            title: "依赖项",
            dataIndex: "dependency",
        },
        {
            title: "执行时机",
            dataIndex: "executionTime",
        },
        {
            title: "适用场景",
            dataIndex: "applicationScenario",
        },
    ]

    const TableData = [
        {
            dependency: "无依赖",
            executionTime: "每次组件重新渲染/初始化 都会执行副作用",
            applicationScenario: "需要实时响应所有状态变化的操作（如DOM同步、日志记录）",
        },
        {
            dependency: "空数组",
            executionTime: "仅第一次组件挂载时执行副作用",
            applicationScenario: "数据请求、事件监听器设置、手动修改DOM、一次性资源加载（如地图SDK初始化）",
        },
        {
            dependency: "依赖项",
            executionTime: "当依赖项改变时(包含初始化的1次)执行副作用",
            applicationScenario: "需要根据特定状态变化执行操作（如数据获取、订阅更新）",
        },
        {
            dependency: "返回函数",
            executionTime: "组件卸载/更新时执行1次",
            applicationScenario: "需要清理副作用（如定时器、事件监听器）",
        },
    ]

    // 7.自定义hook  自定义hook是一个函数，以use开头，用于封装常用的逻辑。自定义hook可以复用逻辑，提高代码的可维护性。
    const [isShow, setIsShow] = useToggle(false)

    return (
        <div className="main_box">
            <h1>本案例展示 react常用的hooks</h1>

            <Tabs
                defaultActiveKey="useState"
                items={[
                    {
                        label: "useState",
                        key: "useState",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>useState 用于声明数据，数据改变会触发组件重新渲染。useState的参数是初始值，返回一个数组，第一个元素是当前值，第二个元素是更新函数</p>

                                    <div>显示state数据：{num}</div>
                                    <Button onClick={updateData}>更新state数据</Button>
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "useRef",
                        key: "useRef",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>useRef 用于声明一个可变的对象，数据改变不会触发组件重新渲染。常用于获取DOM元素或者保存一个数据(计时器、事件监听等)，这个数据在组件重新渲染时不会丢失。</p>
                                    <Input placeholder="请输入" type="text" ref={inputRef} style={{ width: "200px" }} />
                                    <Button onClick={() => inputRef.current.focus()}>获取焦点</Button>
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "useReducer",
                        key: "useReducer",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>useReducer 用于处理复杂的state逻辑，通过dispatch函数触发state更新，类似于redux。相较于useState，逻辑更集中，便于维护。</p>
                                    <div>显示state数据：{state.count}</div>
                                    <Button onClick={() => dispatch({ type: "increment" })}>增加</Button>
                                    <Button onClick={() => dispatch({ type: "decrement" })}>减少</Button>
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "useMemo",
                        key: "useMemo",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>useMemo 用于缓存计算结果，只有当依赖项改变时才会重新计算(其他依赖改变导致的组件重新渲染，不会触发计算)。可以避免不必要的计算，提高性能。</p>
                                    <p>子组件用memo包裹，可以实现子组件的缓存（只有useMemo更新才会渲染子组件，父组件其他依赖改变不会触发子组件重新渲染）。</p>
                                    <p>userMemo有一点像vue的计算属性，但是vue的计算属性是响应式的。useMemo使用场景：昂贵的计算、子组件缓存。如果简单的计算就没必要用，使用前要评估是否值得使用。</p>
                                    <div>显示useMemo数据(改变会触发子组件重新渲染)：{memoizedValue}</div>
                                    <div>显示非useMemo数据(改变不会触发子组件重新渲染)：{state.count}</div>
                                    <MemoSon data={memoizedValue} />
                                    <Button onClick={() => setArr([...arr, Math.floor(Math.random() * 10) + 1])}>useMemo增加</Button>
                                    <Button onClick={() => dispatch({ type: "increment" })}>非useMemo增加</Button>
                                    <Button onClick={() => dispatch({ type: "decrement" })}>非useMemo减少</Button>
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "useCallback",
                        key: "useCallback",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>useCallback 用于缓存函数，只有当依赖项改变时才会重新生成函数。可以避免不必要的函数重新生成，提高性能。</p>
                                    <p>无依赖时,每次都重新创建函数，等于未使用useCallback; 非空数组,仅依赖变化时才会重新创建函数,后面一直使用缓存值; 空数组,仅第一次创建函数,后面一直使用缓存值。</p>
                                    {/* 使用useCallback例子,只有依赖的num改变时，才会重新渲染子组件 */}
                                    <MemoSon fn={memoizedCallback} />
                                    {/* 子组件有缓存，所以改变num2不会触发子组件重新渲染 */}
                                    <Button onClick={() => updateNum2()}>更新num2</Button>
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "useEffect",
                        key: "useEffect",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>
                                        useEffect
                                        用于处理副作用，如数据获取、订阅或者手动更改DOM。useEffect的第二个参数是依赖项，当依赖项改变时才会执行副作用。如果不传依赖项，则每次组件重新渲染都会执行副作用。
                                    </p>
                                    {/* 用表格显示 */}
                                    <Table columns={TableColumns} dataSource={TableData} />
                                </div>
                            </>
                        ),
                    },
                    {
                        label: "自定义hook",
                        key: "自定义hook",
                        children: (
                            <>
                                <div className="main_row">
                                    <p>自定义hook是一个函数，以use开头，用于封装常用的逻辑。自定义hook可以复用逻辑，提高代码的可维护性。</p>
                                    {/* 7个常见的自定义hooks   https://juejin.cn/post/7464247481227149348 */}
                                    <p>自定义hooks可以用于封装axios请求、防抖等公用逻辑。</p>
                                    <div>显示state数据：{isShow ? "显示" : "隐藏"}</div>
                                    <Button onClick={() => setIsShow()}>切换useToggle</Button>
                                </div>
                            </>
                        ),
                    },
                ]}
            ></Tabs>

            <div className="main_text"></div>
        </div>
    )
}

// 自定义hook  自定义hook是一个函数，以use开头，用于封装常用的逻辑。自定义hook可以复用逻辑，提高代码的可维护性。
const useToggle = (initialState = false) => {
    const [state, setState] = useState(initialState)
    const toggle = () => setState(!state)
    return [state, toggle]
}

// 子组件
const MemoSon = memo(({ data, fn }) => {
    console.log("子组件渲染")
    return (
        <div>
            {data ? `子组件-${data}` : ""}
            {fn ? <Button onClick={() => fn()}>子组件按钮</Button> : ""}
        </div>
    )
})

export default Demo8
